package com.nvelickovic10.snake.beans.snake;

import java.awt.Graphics2D;
import java.awt.Point;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.JOptionPane;

import com.nvelickovic10.snake.beans.food.Bonus;
import com.nvelickovic10.snake.beans.food.BonusType;
import com.nvelickovic10.snake.beans.game.HiScore;
import com.nvelickovic10.snake.main.AppCore;

public class Snake {
	private static Snake instance = null;
	private ArrayList<SnakeBits> body;
	private SnakeBits head;

	private Snake() {
		restart();
	}

	public static Snake getInstance() {
		if (instance == null)
			instance = new Snake();
		return instance;
	}

	public void setBody(ArrayList<SnakeBits> body) {
		this.body = body;
		setHead(body.get(0));
	}

	public void setHead(SnakeBits head) {
		this.head = head;
	}

	public synchronized void addBit() {
		SnakeBits last = body.get(body.size() - 1);
		Direction lastDirection = last.getDirrection();
		int x = (int) last.getPosition().getX(), y = (int) last.getPosition()
				.getY();
		Point position = null;
		switch (lastDirection) {
		case LEFT:
			position = new Point(x + 1, y);
			break;
		case RIGHT:
			position = new Point(x - 1, y);
			break;
		case UP:
			position = new Point(x, y + 1);
			break;
		case DOWN:
			position = new Point(x, y - 1);
			break;
		default:
			break;
		}
		SnakeBits bit = new SnakeBits(position);
		bit.setDirrection(lastDirection);
		body.add(bit);
	}

	public synchronized void turnSnake(Direction direction) {
		for (int i = body.size() - 1; i > 0; i--) {
			body.get(i).setPosition(
					(Point) body.get(i - 1).getPosition().clone());
			body.get(i).setDirrection(body.get(i - 1).getDirrection());
		}

		switch (direction) {
		case LEFT:
			head.setPosition(new Point((int) head.getPosition().getX() - 1,
					(int) head.getPosition().getY()));
			break;
		case RIGHT:
			head.setPosition(new Point((int) head.getPosition().getX() + 1,
					(int) head.getPosition().getY()));
			break;
		case UP:
			head.setPosition(new Point((int) head.getPosition().getX(),
					(int) head.getPosition().getY() - 1));
			break;
		case DOWN:
			head.setPosition(new Point((int) head.getPosition().getX(),
					(int) head.getPosition().getY() + 1));
			break;
		default:
			break;
		}
		BonusType ret = isHeadOnBonus();
		if (ret != null) {
			if (ret == BonusType.YELLOW_BONUS) {
				if ((body.size() % (AppCore.getInstance().getWallFaktor() - 1)) == 0)
					AppCore.getInstance().switchBoardColor();
				if (body.size() > 3)
					body.remove(body.size() - 1);
				AppCore.getInstance().setPoints(
						(int) ((AppCore.getInstance().getPoints() + 10) * 1.3));
				AppCore.getInstance().getBonus(BonusType.YELLOW_BONUS).setOff();
			} else if (ret == BonusType.BLUE_BONUS) {
				if (AppCore.getInstance().getWalls().size() > 4)
					for (int i = 0; i < 5; i++)
						AppCore.getInstance().getWalls().remove(0);
				AppCore.getInstance().setPoints(
						(int) ((AppCore.getInstance().getPoints() + 10) * 1.3));
				AppCore.getInstance().getBonus(BonusType.BLUE_BONUS).setOff();
			} else if (ret == BonusType.MAGENTA_BONUS) {
				AppCore.getInstance().setPoints(
						(int) ((AppCore.getInstance().getPoints()) * 1.5));
				AppCore.getInstance().getBonus(BonusType.MAGENTA_BONUS)
						.setOff();
			} else if (ret == BonusType.GREEN_BONUS) {
				AppCore.getInstance().setPoints(
						(int) ((AppCore.getInstance().getPoints() + 10) * 1.3));
				AppCore.getInstance().slowSpeed();
				AppCore.getInstance().getBonus(BonusType.GREEN_BONUS).setOff();
			}
		}

		if (isHeadOnFood()) {
			try {
				Clip clip = AudioSystem.getClip();
				AudioInputStream inputStream = AudioSystem
						.getAudioInputStream(new File("sound/eatSound.wav"));
				clip.open(inputStream);
				clip.start();
			} catch (LineUnavailableException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (UnsupportedAudioFileException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			addBit();

			AppCore.getInstance().setPoints(
					(int) ((AppCore.getInstance().getPoints() + 10) * 1.1));
			AppCore.getInstance().setSpeed();

			if (body.size() % AppCore.getInstance().getWallFaktor() == 0) {
				for (int i = 0; i < 5; i++) {
					AppCore.getInstance().getWalls()
							.add(body.get(body.size() - 1));
					body.remove(body.size() - 1);
				}
				AppCore.getInstance().setWallFaktor(
						AppCore.getInstance().getWallFaktor() + 2);
				AppCore.getInstance().switchBoardColor();
			}
			if ((body.size() % (AppCore.getInstance().getWallFaktor() - 1)) == 0)
				AppCore.getInstance().switchBoardColor();
			AppCore.getInstance().setFoodPosition();
		}

		if (isHeadOnSelf() || isHeadOnWall()
				|| head.getPosition().getX() > AppCore.getInstance().getWd()
				|| head.getPosition().getX() < 1
				|| head.getPosition().getY() < 1
				|| head.getPosition().getY() > AppCore.getInstance().getHg()) {
			if (!AppCore.getInstance().isDeveloperVersion())
				AppCore.getInstance().setGameOn(false);
			AppCore.getInstance().setGameOn(false);
			AppCore.getInstance().loadHiscore();
			HiScore hs = new HiScore(AppCore.getInstance().getUsername(),
					AppCore.getInstance().getPoints());
			AppCore.getInstance().getHiscore().add(hs);
			AppCore.getInstance().setHiscore();

			String message = "GAME OVER\nTOTAL POINTS: "
					+ AppCore.getInstance().getPoints()
					+ "\n\n=============\nHI SCORES:\nposition - points - username - date";
			int i = 0;
			for (HiScore hd : AppCore.getInstance().getHiscore()) {
				message += "\n" + ++i + " - " + hd.getPoints() + " - "
						+ hd.getUsername() + " - " + hd.getDate();
			}
			message += "\n=============\n\nNEW GAME?";

			int resp = JOptionPane.showConfirmDialog(null, message,
					"GAME OVER", JOptionPane.YES_NO_OPTION);
			if (resp == JOptionPane.YES_OPTION) {
				AppCore.getInstance().restart();
			} else {
				AppCore.getInstance().getGw().dispose();
			}
		}
	}

	private boolean isHeadOnSelf() {
		for (int i = 1; i < body.size(); i++)
			if (head.getPosition().getX() == body.get(i).getPosition().getX()
					&& head.getPosition().getY() == body.get(i).getPosition()
							.getY())
				return true;
		return false;
	}

	public boolean isHeadOnFood() {
		if (AppCore.getInstance().getFood().getPosition().getX() == head
				.getPosition().getX()
				&& AppCore.getInstance().getFood().getPosition().getY() == head
						.getPosition().getY())
			return true;
		return false;
	}

	public BonusType isHeadOnBonus() {
		for (Bonus bonus : AppCore.getInstance().getBonuses())
			if (bonus.isOn()
					&& bonus.getPosition().getX() == head.getPosition().getX()
					&& bonus.getPosition().getY() == head.getPosition().getY())
				return bonus.getType();
		return null;
	}

	public boolean isHeadOnWall() {
		for (SnakeBits bit : AppCore.getInstance().getWalls())
			if (bit.getPosition().getX() == head.getPosition().getX()
					&& bit.getPosition().getY() == head.getPosition().getY())
				return true;
		return false;
	}

	public ArrayList<SnakeBits> getBody() {
		return body;
	}

	public void paintSnake(Graphics2D g) {
		for (int i = body.size() - 1; i >= 0; i--)
			body.get(i).paintBit(g);
	}

	public SnakeBits getHead() {
		return head;
	}

	public void restart() {
		body = new ArrayList<>();
		head = new SnakeBits(new Point(AppCore.getInstance().getWd(), AppCore
				.getInstance().getHg() - 2));
		head.setHead(true);
		body.add(head);
		body.add(new SnakeBits(new Point(AppCore.getInstance().getWd(), AppCore
				.getInstance().getHg() - 1)));
		body.add(new SnakeBits(new Point(AppCore.getInstance().getWd(), AppCore
				.getInstance().getHg())));
	}

}
